#!/usr/bin/env python3

#   Copyright (C) 2014  Michal Fapso <michal.fapso@gmail.com>
#   Copyright (C) 2014  Povilas Kanapickas <povilas@radix.lt>
#
# Distributed under the Boost Software License, Version 1.0.
#   (See accompanying filename LICENSE_1_0.txt or copy at
#          http://www.boost.org/LICENSE_1_0.txt)

# Generates the simdpp/operator/*.h filenames
# Use as $ ./tools/gen_operators.py

import os.path
import re

class Config:
    filename = None
    fun = None
    op = None
    fun_regex = None

    def __init__(self, filename, fun, op, fun_regex = None):
        self.filename = filename
        self.fun = fun
        self.op = op
        if fun_regex:
            self.fun_regex = fun_regex
        else:
            self.fun_regex = r"\b" + fun + r"\b"


config = {}

#--------------------------------------------------
# CONFIG
#--------------------------------------------------
def add_to_config(filename, fun, op, fun_regex = None):
    global config
    config[filename] = Config(filename, fun, op, fun_regex)

add_to_config("i_shift_l.h", "shift_l", "<<", r'\bshift_l\b.*\bcount\b');
add_to_config("i_shift_r.h", "shift_r", ">>", r'\bshift_r\b.*\bcount\b');
add_to_config("cmp_eq.h" , "cmp_eq" , "==");
add_to_config("cmp_neq.h", "cmp_neq", "!=");
add_to_config("cmp_ge.h" , "cmp_ge" , ">=");
add_to_config("cmp_gt.h" , "cmp_gt" , ">" );
add_to_config("cmp_le.h" , "cmp_le" , "<=");
add_to_config("cmp_lt.h" , "cmp_lt" , "<" );
add_to_config("f_add.h", "add", "+");
add_to_config("i_add.h", "add", "+");
add_to_config("f_sub.h", "sub", "-");
add_to_config("i_sub.h", "sub", "-");
add_to_config("f_mul.h", "mul"   , "*");
add_to_config("i_mul.h", "mul_lo", "*");
add_to_config("f_div.h", "div", "/");
add_to_config("bit_or.h"     , "bit_or"     , "|" );
add_to_config("bit_and.h"    , "bit_and"    , "&" );
add_to_config("bit_not.h"    , "bit_not"    , "~" );
add_to_config("bit_xor.h"    , "bit_xor"    , "^" );

#--------------------------------------------------
# PROCESS FILES
#--------------------------------------------------
for rec in config:
    c = config[rec]

    curly_braces_counter = 0

    if not os.path.exists("simdpp/operators"):
        os.path.makedirs("simdpp/operators")

    file_out = "simdpp/operators/" + config[rec].filename
    file_in = "simdpp/core/" + config[rec].filename

    out_f = open(file_out, "w")
    in_f = open(file_in, "r")

    if not out_f:
        print("Unable to open the file " + file_out)
        sys.exit(1)
    if not in_f:
        print("Unable to open the file " + file_in)
        sys.exit(1)

    out_f.write("// This file is generated by tools/gen_operators.pl. CHANGES WILL BE OVERWRITTEN\n")

	# Copy the original source filename, replacing its function names with operators

    last_comment_begin = None

    lines = in_f.readlines()
    in_f.close()

    def kill_function(i, lines, last_comment_begin):
        # find the end of the function
        while i < len(lines):
            if re.search(r'^\s*\}\s*$', lines[i]):
                while i < len(lines):
                    if (re.search(r'^\s*#endif\s*$', lines[i+1]) or
                        re.search(r'^\s*SIMDPP_SCALAR_ARG', lines[i+1]) or
                        re.search(r'^\s*$', lines[i+1])):
                        i += 1
                        continue
                    break
                break
            i += 1

        if i == len(lines):
            raise Exception("Reached end of file, but end of function not found")

        lines = lines[:last_comment_begin] + lines[i+1:]
        return (last_comment_begin, lines)

    i = 0
    while i < len(lines):
        line = lines[i]

        # all functions are preceded by comments
        if re.search(r"/\*\*", line):
            last_comment_begin = i

        if re.search(r"#(ifndef|define) LIBSIMDPP_SIMDPP_.*_H", line):
            line = re.sub(r"(#(?:ifndef|define) LIBSIMDPP_SIMDPP_.*)_H", r"\1_OPERATOR_H", line)

		# Workaround: mul_hi in i_mul.h has to be disabled, because it is not used for an operator
        if (config[rec].filename == "i_mul.h" and
            re.search(r'\bmul_hi\b', line) and not re.search(r'#include', line)):
            (i, lines) = kill_function(i, lines, last_comment_begin)
            continue

        if re.search(r'\b' + c.fun + r'\b', line) and not re.search(r'#include', line):
            if re.search(c.fun_regex, line):
                line = re.sub(r'\b' + c.fun + r'\b', 'operator' + c.op, line)
            else:
                 # Disable functions which don't match the specified regex
                 (i, lines) = kill_function(i, lines, last_comment_begin)
                 continue

        lines[i] = line
        i += 1

    out_f.writelines(lines)
    out_f.close()
